home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / dns / host / send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-23  |  13.6 KB  |  631 lines

  1. /*
  2.  * Copyright (c) 1985, 1989 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that: (1) source distributions retain this entire copyright
  7.  * notice and comment, and (2) distributions including binaries display
  8.  * the following acknowledgement:  ``This product includes software
  9.  * developed by the University of California, Berkeley and its contributors''
  10.  * in the documentation or other materials provided with the distribution
  11.  * and in all advertising materials mentioning features or use of this
  12.  * software. Neither the name of the University nor the names of its
  13.  * contributors may be used to endorse or promote products derived
  14.  * from this software without specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. #ifndef lint
  21. static char Version[] = "@(#)send.c    e07@nikhef.nl (Eric Wassenaar) 920924";
  22. #endif
  23.  
  24. #include <stdio.h>
  25. #include <errno.h>
  26. #include <setjmp.h>
  27. #include <signal.h>
  28. #include <sys/time.h>
  29.  
  30. #include <sys/param.h>
  31. #include <sys/socket.h>
  32. #include <netinet/in.h>
  33. #include <arpa/nameser.h>
  34. #include <resolv.h>
  35.  
  36. #define input            /* read-only input parameter */
  37. #define output            /* modified output parameter */
  38.  
  39. #if defined(sparc) || defined(hpux) || defined(_AIX) || defined(ultrix)
  40. typedef void    sig_t;
  41. #else
  42. typedef int    sig_t;
  43. #endif
  44.  
  45. #define bitset(a,b)    (((a) & (b)) != 0)
  46.  
  47. #ifdef ULTRIX_RESOLV
  48. #define nslist(i)    _res.ns_list[i].addr
  49. #else
  50. #define nslist(i)    _res.nsaddr_list[i]
  51. #endif
  52.  
  53. extern int errno;
  54. extern struct state _res;    /* defined in res_init.c */
  55.  
  56. static int timeout;        /* connection read timeout */
  57.  
  58. extern char *inet_ntoa();
  59.  
  60. /* send.c */
  61. int res_send();
  62. #ifndef lint
  63. void _res_close();
  64. #endif
  65. static int send_stream();
  66. static int send_dgram();
  67. static sig_t timer();
  68. int _res_connect();
  69. int _res_write();
  70. int _res_read();
  71. static int recvsock();
  72.  
  73. /*
  74. ** RES_SEND -- Send nameserver query and retrieve answer
  75. ** -----------------------------------------------------
  76. **
  77. **    Returns:
  78. **        Length of nameserver answer buffer, if obtained.
  79. **        -1 if an error occurred (errno set appropriately).
  80. **
  81. **    This is a simplified version of the BIND 4.8.3 res_send().
  82. **    - Always use connected datagrams to get proper error messages.
  83. **    - Do not only return ETIMEDOUT or ECONNREFUSED in datagram mode.
  84. **    - Never leave a connection open after we got an answer.
  85. **    - No special ECONNRESET handling when using virtual circuits.
  86. */
  87.  
  88. int
  89. res_send(query, querylen, answer, anslen)
  90. input char *query;            /* address of formatted query buffer */
  91. input int querylen;            /* length of query buffer */
  92. output char *answer;            /* address of buffer to store answer */
  93. input int anslen;            /* maximum size of answer buffer */
  94. {
  95.     HEADER *bp = (HEADER *)answer;
  96.     struct sockaddr_in *sin;
  97.     int v_circuit;            /* virtual circuit or datagram switch */
  98.     register int try, ns;
  99.     register int n;
  100.  
  101.     /* make sure resolver has been initialized */
  102.     if (!bitset(RES_INIT, _res.options) && res_init() == -1)
  103.         return(-1);
  104.  
  105.     if (bitset(RES_DEBUG, _res.options))
  106.     {
  107.         printf("res_send()\n");
  108.         p_query(query);
  109.     }
  110.  
  111.     /* use virtual circuit if requested or if necessary */
  112.     v_circuit = bitset(RES_USEVC, _res.options) || (querylen > PACKETSZ);
  113.  
  114. /*
  115.  * Do _res.retry attempts for each of the _res.nscount addresses.
  116.  */
  117.     for (try = 0; try < _res.retry; try++)
  118.     {
  119.         for (ns = 0; ns < _res.nscount; ns++)
  120.         {
  121.         sin = &nslist(ns);
  122. retry:
  123.         if (bitset(RES_DEBUG, _res.options))
  124.             printf("Querying server (# %d) %s address = %s\n", ns+1,
  125.                 v_circuit ? "tcp" : "udp", inet_ntoa(sin->sin_addr));
  126.  
  127.         if (v_circuit)
  128.         {
  129.             /* at most one attempt per server */
  130.             try = _res.retry;
  131.  
  132.             /* connect via virtual circuit */
  133.             n = send_stream(sin, query, querylen, answer, anslen);
  134.         }
  135.         else
  136.         {
  137.             /* set datagram read timeout for recvsock */
  138.             timeout = (_res.retrans << try);
  139.             if (try > 0)
  140.                 timeout /= _res.nscount;
  141.             if (timeout <= 0)
  142.                 timeout = 1;
  143.  
  144.             /* connect via datagram */
  145.             n = send_dgram(sin, query, querylen, answer, anslen);
  146.  
  147.             /* check truncation; use v_circuit with same server */
  148.             if (n > 0 && bp->tc)
  149.             {
  150.                 if (bitset(RES_DEBUG, _res.options))
  151.                     (void) fprintf(stderr, "truncated answer\n");
  152.  
  153.                 if (!bitset(RES_IGNTC, _res.options))
  154.                 {
  155.                     v_circuit = 1;
  156.                     goto retry;
  157.                 }
  158.             }
  159.         }
  160.  
  161.         if (n <= 0)
  162.             continue;
  163.  
  164.         if (bitset(RES_DEBUG, _res.options))
  165.         {
  166.             printf("got answer:\n");
  167.             p_query(answer);
  168.         }
  169.  
  170.         /* we have an answer; clear possible error condition */
  171.         errno = 0;
  172.         return(n);
  173.         }
  174.     }
  175.  
  176.     return(-1);
  177. }
  178.  
  179.  
  180. /*
  181.  * Note that this private version of res_send() is not only called
  182.  * directly by 'host' but also indirectly by gethostbyname() or by
  183.  * gethostbyaddr() via their resolver interface routines.
  184.  */
  185.  
  186.  
  187. /*
  188.  * Provide dummy routine to prevent the real res_send() to be loaded.
  189.  * This one is actually only called by endhostent() to close a socket
  190.  * that was requested to stay open. But in this version sockets are
  191.  * always closed after use.
  192.  */
  193.  
  194. #ifndef lint
  195.  
  196. void
  197. _res_close()
  198. {
  199. }
  200.  
  201. #endif
  202.  
  203. /*
  204. ** SEND_STREAM -- Query nameserver via virtual circuit
  205. ** ---------------------------------------------------
  206. **
  207. **    Returns:
  208. **        Length of nameserver answer buffer, if obtained.
  209. **        -1 if an error occurred.
  210. **
  211. **    Note that connect() is the call that is allowed to fail
  212. **    under normal circumstances. All other failures generate
  213. **    an unconditional error message.
  214. */
  215.  
  216. static int
  217. send_stream(addr, query, querylen, answer, anslen)
  218. input struct sockaddr_in *addr;        /* the server address to connect to */
  219. input char *query;            /* address of formatted query buffer */
  220. input int querylen;            /* length of query buffer */
  221. output char *answer;            /* address of buffer to store answer */
  222. input int anslen;            /* maximum size of answer buffer */
  223. {
  224.     int sock;
  225.     register int n;
  226.  
  227. /*
  228.  * Setup a virtual circuit connection.
  229.  */
  230.     sock = socket(AF_INET, SOCK_STREAM, 0);
  231.     if (sock < 0)
  232.     {
  233.         perror("socket");
  234.         return(-1);
  235.     }
  236.  
  237.     if (_res_connect(sock, addr, sizeof(*addr)) < 0)
  238.     {
  239.         if (bitset(RES_DEBUG, _res.options))
  240.             perror("connect");
  241.         (void) close(sock);
  242.         return(-1);
  243.     }
  244.  
  245.     if (bitset(RES_DEBUG, _res.options))
  246.         printf("connected to %s\n", inet_ntoa(addr->sin_addr));
  247.  
  248. /*
  249.  * Send the query buffer.
  250.  */
  251.     if (_res_write(sock, query, querylen) < 0)
  252.     {
  253.         (void) close(sock);
  254.         return(-1);
  255.     }
  256.  
  257. /*
  258.  * Read the answer buffer.
  259.  */
  260.     n = _res_read(sock, answer, anslen);
  261.     if (n <= 0)
  262.     {
  263.         (void) close(sock);
  264.         return(-1);
  265.     }
  266.  
  267. /*
  268.  * Never leave the socket open.
  269.  */
  270.     (void) close(sock);
  271.     return(n);
  272. }
  273.  
  274. /*
  275. ** SEND_DGRAM -- Query nameserver via datagram
  276. ** -------------------------------------------
  277. **
  278. **    Returns:
  279. **        Length of nameserver answer buffer, if obtained.
  280. **        -1 if an error occurred.
  281. **
  282. **    Inputs:
  283. **        The global variable timeout should have been
  284. **        set with the desired timeout value in seconds.
  285. **
  286. **    Sending to a nameserver datagram port with no nameserver running
  287. **    will cause an ICMP port unreachable message to be returned. If the
  288. **    socket is connected, we get an ECONNREFUSED error on the next socket
  289. **    operation, and select returns if the error message is received.
  290. **    Also, we get ENETUNREACH or EHOSTUNREACH errors if appropriate.
  291. **    We thus get a proper error status before timing out.
  292. **    This method usually works only if BSD >= 43.
  293. **
  294. **    Note that recv() is now the call that is allowed to fail
  295. **    under normal circumstances. All other failures generate
  296. **    an unconditional error message.
  297. */
  298.  
  299. static int
  300. send_dgram(addr, query, querylen, answer, anslen)
  301. input struct sockaddr_in *addr;        /* the server address to connect to */
  302. input char *query;            /* address of formatted query buffer */
  303. input int querylen;            /* length of query buffer */
  304. output char *answer;            /* address of buffer to store answer */
  305. input int anslen;            /* maximum size of answer buffer */
  306. {
  307.     HEADER *qp = (HEADER *)query;
  308.     HEADER *bp = (HEADER *)answer;
  309.     int sock;
  310.     register int n;
  311.  
  312. /*
  313.  * Setup a connected datagram socket.
  314.  */
  315.     sock = socket(AF_INET, SOCK_DGRAM, 0);
  316.     if (sock < 0)
  317.     {
  318.         perror("socket");
  319.         return(-1);
  320.     }
  321.  
  322.     if (connect(sock, (struct sockaddr *)addr, sizeof(*addr)) < 0)
  323.     {
  324.         perror("connect");
  325.         (void) close(sock);
  326.         return(-1);
  327.     }
  328.  
  329. /*
  330.  * Send the query buffer.
  331.  */
  332.     if (send(sock, query, querylen, 0) != querylen)
  333.     {
  334.         perror("send");
  335.         (void) close(sock);
  336.         return(-1);
  337.     }
  338.  
  339. /*
  340.  * Wait for the arrival of a reply, timeout, or error message.
  341.  */
  342. wait:
  343.     n = recvsock(sock, answer, anslen);
  344.     if (n <= 0)
  345.     {
  346.         if (bitset(RES_DEBUG, _res.options))
  347.             perror("recvfrom");
  348.         (void) close(sock);
  349.         return(-1);
  350.     }
  351.  
  352. /*
  353.  * Make sure it is the proper response by checking the packet id.
  354.  */
  355.     if (qp->id != bp->id)
  356.     {
  357.         if (bitset(RES_DEBUG, _res.options))
  358.         {
  359.             printf("old answer:\n");
  360.             p_query(answer);
  361.         }
  362.         goto wait;
  363.     }
  364.  
  365. /*
  366.  * Never leave the socket open.
  367.  */
  368.     (void) close(sock);
  369.     return(n);
  370. }
  371.  
  372. /*
  373. ** _RES_CONNECT -- Connect to a stream (virtual circuit) socket
  374. ** ------------------------------------------------------------
  375. **
  376. **    Returns:
  377. **        0 if successfully connected.
  378. **        -1 in case of failure or timeout.
  379. **
  380. **    Note that we use _res.retrans to override the default
  381. **    connect timeout value.
  382. */
  383.  
  384. static jmp_buf timer_buf;
  385.  
  386. static sig_t
  387. /*ARGSUSED*/
  388. timer(sig)
  389. int sig;
  390. {
  391.     longjmp(timer_buf, 1);
  392. }
  393.  
  394.  
  395. int
  396. _res_connect(sock, addr, addrlen)
  397. input int sock;
  398. input struct sockaddr_in *addr;        /* the server address to connect to */
  399. input int addrlen;
  400. {
  401.     if (setjmp(timer_buf) != 0)
  402.     {
  403.         errno = ETIMEDOUT;
  404.         (void) alarm((u_int)0);
  405.         return(-1);
  406.     }
  407.  
  408.     (void) signal(SIGALRM, timer);
  409.     (void) alarm((u_int)_res.retrans);
  410.  
  411.     if (connect(sock, (struct sockaddr *)addr, addrlen) < 0)
  412.     {
  413.         if (errno == EINTR)
  414.             errno = ETIMEDOUT;
  415.         (void) alarm((u_int)0);
  416.         return(-1);
  417.     }
  418.  
  419.     (void) alarm((u_int)0);
  420.     return(0);
  421. }
  422.  
  423. /*
  424. ** _RES_WRITE -- Write the query buffer via a stream socket
  425. ** --------------------------------------------------------
  426. **
  427. **    Returns:
  428. **        Length of buffer if successfully transmitted.
  429. **        -1 in case of failure (error message is issued).
  430. **
  431. **    The query is sent in two steps: first a single word with
  432. **    the length of the buffer, followed by the buffer itself.
  433. */
  434.  
  435. int
  436. _res_write(sock, buf, bufsize)
  437. input int sock;
  438. input char *buf;            /* address of formatted query buffer */
  439. input int bufsize;            /* length of query buffer */
  440. {
  441.     u_short len;
  442.  
  443. /*
  444.  * Write the length of the query buffer.
  445.  */
  446.     len = htons(bufsize);
  447.  
  448.     if (write(sock, (char *)&len, sizeof(len)) != sizeof(len))
  449.     {
  450.         perror("write query length");
  451.         return(-1);
  452.     }
  453.  
  454. /*
  455.  * Write the query buffer itself.
  456.  */
  457.     if (write(sock, buf, bufsize) != bufsize)
  458.     {
  459.         perror("write query");
  460.         return(-1);
  461.     }
  462.  
  463.     return(bufsize);
  464. }
  465.  
  466. /*
  467. ** _RES_READ -- Read the answer buffer via a stream socket
  468. ** -------------------------------------------------------
  469. **
  470. **    Returns:
  471. **        Length of buffer if successfully received.
  472. **        -1 in case of failure (error message is issued).
  473. **
  474. **    The answer is read in two steps: first a single word which
  475. **    gives the length of the buffer, followed by the buffer itself.
  476. **    If the answer is too long to fit into the supplied buffer,
  477. **    only the portion that fits will be stored, the residu will be
  478. **    flushed, and the truncation flag will be set.
  479. */
  480.  
  481. int
  482. _res_read(sock, buf, bufsize)
  483. input int sock;
  484. output char *buf;            /* address of buffer to store answer */
  485. input int bufsize;            /* maximum size of answer buffer */
  486. {
  487.     u_short len;
  488.     char *buffer;
  489.     int buflen;
  490.     int reslen;
  491.     register int n;
  492.  
  493.     /* set stream timeout for recvsock */
  494.     timeout = 60;
  495.  
  496. /*
  497.  * Read the length of answer buffer.
  498.  */
  499.     buffer = (char *)&len;
  500.     buflen = sizeof(len);
  501.  
  502.     while (buflen > 0 && (n = recvsock(sock, buffer, buflen)) > 0)
  503.     {
  504.         buffer += n;
  505.         buflen -= n;
  506.     }
  507.  
  508.     if (buflen != 0)
  509.     {
  510.         perror("read answer length");
  511.         return(-1);
  512.     }
  513.  
  514. /*
  515.  * Terminate if length is zero.
  516.  */
  517.     len = ntohs(len);
  518.     if (len == 0)
  519.         return(0);
  520.  
  521. /*
  522.  * Check for truncation.
  523.  */
  524.     reslen = 0;
  525.     if ((int)len > bufsize)
  526.     {
  527.         reslen = len - bufsize;
  528.         len = bufsize;
  529.     }
  530.  
  531. /*
  532.  * Read the answer buffer itself.
  533.  */
  534.     buffer = buf;
  535.     buflen = len;
  536.  
  537.     while (buflen > 0 && (n = recvsock(sock, buffer, buflen)) > 0)
  538.     {
  539.         buffer += n;
  540.         buflen -= n;
  541.     }
  542.  
  543.     if (buflen != 0)
  544.     {
  545.         perror("read answer");
  546.         return(-1);
  547.     }
  548.  
  549. /*
  550.  * Discard the residu to keep connection in sync.
  551.  */
  552.     if (reslen > 0)
  553.     {
  554.         HEADER *bp = (HEADER *)buf;
  555.         char resbuf[PACKETSZ];
  556.  
  557.         buffer = resbuf;
  558.         buflen = reslen < sizeof(resbuf) ? reslen : sizeof(resbuf);
  559.  
  560.         while (reslen > 0 && (n = recvsock(sock, buffer, buflen)) > 0)
  561.         {
  562.             reslen -= n;
  563.             buflen = reslen < sizeof(resbuf) ? reslen : sizeof(resbuf);
  564.         }
  565.  
  566.         if (reslen != 0)
  567.         {
  568.             perror("read residu");
  569.             return(-1);
  570.         }
  571.  
  572.         if (bitset(RES_DEBUG, _res.options))
  573.             (void) fprintf(stderr, "response truncated\n");
  574.  
  575.         /* set truncation flag */
  576.         bp->tc = 1;
  577.     }
  578.  
  579.     return(len);
  580. }
  581.  
  582. /*
  583. ** RECVSOCK -- Read from stream or datagram socket with timeout
  584. ** ------------------------------------------------------------
  585. **
  586. **    Returns:
  587. **        Length of buffer if successfully received.
  588. **        -1 in case of failure or timeout.
  589. **
  590. **    Inputs:
  591. **        The global variable timeout should have been
  592. **        set with the desired timeout value in seconds.
  593. */
  594.  
  595. static int
  596. recvsock(sock, buffer, buflen)
  597. input int sock;
  598. output char *buffer;            /* current buffer address */
  599. input int buflen;            /* remaining buffer size */
  600. {
  601.     fd_set fds;
  602.     struct timeval wait;
  603.     register int n;
  604.  
  605.     wait.tv_sec = timeout;
  606.     wait.tv_usec = 0;
  607.  
  608.     /* FD_ZERO(&fds); */
  609.     bzero((char *)&fds, sizeof(fds));
  610.     FD_SET(sock, &fds);
  611.  
  612. /*
  613.  * Wait for the arrival of data, or timeout.
  614.  */
  615.     n = select(FD_SETSIZE, &fds, (fd_set *)NULL, (fd_set *)NULL, &wait);
  616.     if (n <= 0)
  617.     {
  618.         if (n == 0)
  619.             errno = ETIMEDOUT;
  620.         return(-1);
  621.     }
  622.  
  623. /*
  624.  * Fake an error if nothing was actually read.
  625.  */
  626.     n = recv(sock, buffer, buflen, 0);
  627.     if (n == 0)
  628.         errno = ECONNRESET;
  629.     return(n);
  630. }
  631.